// 条件内置类型Extract  Exclude  NonNullable
// 循环类型 Partial Required Readonly

type IPerson = {
  name: string;
  age: string;
  company: string;
};

// type Readonly<T> = {
//   +readonly [K in keyof T]: T[K];
// };
// type ReadonlyRes = Readonly<IPerson>;

// // 不能修改就是readonly ， 却掉readonly
// type Mutate<T> = {
//   -readonly [K in keyof T]: T[K];
// };
// type MutateRes = Mutate<ReadonlyRes>;

// -? +?   -readonly +readonly

// Partial
// Omit Pick 对象来操作的
// type Pick<T, K extends keyof T> = {
//   [key in K]: T[key];
// };
type PickRes = Pick<IPerson, "company" | "age">;
// type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
// type OmitRes = Omit<IPerson, "name" | "age">;
// Pick和Omit用的比较多
// Record
// type Record<T extends keyof any, K> = {
//   [key in T]: K;
// };
// string:any 任意类型 索引类型
const obj: Record<string, any> = { b: 1, c: "2" };

// { name: "jw", age: 30 } -> { name: "jw" + 100, age: 30 + 100 }

// 推导出类型 (根据泛型的位置 来推导具体的类型)
// function map<T extends keyof any, K, R>(
//   obj: Record<T, K>,
//   callback: (value: K, key: T) => R
// ) {
//   let result = {} as Record<T, R>;
//   for (let key in obj) {
//     result[key] = callback(obj[key], key);
//   }
//   return result;
// }
// map({ name: "jw", age: 30 }, (value, key) => {
//   return true;
// });

// infer  inference 手动类型推导

function getPerson(a: string, b: number) {
  return { name: "jw", age: 30 };
}
// 类型推断 ， 因为infer 需要extends 关键字， 所以必须构建一个条件
// type ReturnType<T extends (...args: any[]) => any> = T extends (
//   ...args: any[]
// ) => infer R
//   ? R
//   : never;
// infer 关键字需要基于extends
type FnReturnType = ReturnType<typeof getPerson>;
// type Parameters<T extends (...args: any[]) => any> = T extends (
//   ...args: infer P
// ) => any
//   ? P
//   : false;
type FnParamaters = Parameters<typeof getPerson>;

class Person {
  constructor(a: string, b: string) {}
}
// type ConstructorParameters<T extends { new (...args: any[]): any }> =
//   T extends { new (...args: infer P): any } ? P : any;

type ClassConstructorParameters = ConstructorParameters<typeof Person>;

// type InstanceType<T extends { new (...args: any[]): any }> = T extends {
//   new (...args: any[]): infer R;
// }
//   ? R
//   : any;
type ClassInstanceType = InstanceType<typeof Person>;

// 如何讲一个元组转化成联合和类型

type ITumple = [string, number, boolean]; // string | number | boolean
// type TumpleToUniom = ITumple[number];

type ElementOf<T extends any[]> = T extends Array<infer R> ? R : never;
type TumpleToUniom = ElementOf<ITumple>;

// 对元组进行参数移动， 将元组头尾进行交换 . infer 要配合 extends
type SwapHeadTail<T extends any[]> = T extends [
  infer Head,
  ...infer Body,
  infer Tail
]
  ? [Tail, ...Body, Head]
  : never;

type Res = SwapHeadTail<["jw", 1, 2, 3, 4, 5, 30]>;

// function getVal(): Promise<Promise<200>> {
//   return new Promise((resolve, reject) => {
//     resolve(
//       new Promise((resolve, reject) => {
//         resolve(200);
//       }) as Promise<200>
//     );
//   });
// }

// infer 可以递归推断
type PromiseVal<T> = T extends Promise<infer R> ? PromiseVal<R> : T;
type PromiseReturnVal = PromiseVal<Promise<Promise<200>>>;

// class Cat {
//   constructor(public name: string) {}
// }
// class Dog {
//   constructor(public name: string) {}
// }
// function createInstance<T extends { new (...args: any[]): any }>(
//   clazz: T,
//   name: string
// ): InstanceType<T> {
//   let r = new clazz(name);
//   return r;
// }
// const instance = createInstance(Dog, "tom");
export {};
// .................................................................................
//正常判断类型的时候 可以通过 A extends BA是B的子类型
//条件分发(分发特性是默认开启的)要满足下面三个条件
//1.A类型是通过泛型传入的 <T>
//2.A类型如果是联合类型会进行分发 b|c
//3.泛型参数A 必须是完全裸露的，才具备分发的能力===>只要让A,不是裸类型,就会丧失这种分发机制  A & {}
// 裸类型==>只有自己,自己没有和别人发生关系,  常见的发生关系 T & {} 穿衣, [T]包装原数组, T[]包装数组
//我们在进行类型父子关系的比较时，默认情况下都应该关闭分发
interface Bird {
  name: "鸟";
}
interface Sky {
  name: "天";
}
interface Fish {
  name: "鱼";
}
interface Water {
  name: "水";
}
// 这个写法是不会导致分发,不是泛型传入, Fish | Bird的层级明显大于Fish
type conditional1<T> = Fish | Bird extends Fish ? Sky : Water;

type Conditional2<T> = T extends Fish ? Water : Sky;
// Conditional2<Fish | Bird>;将联合类型中的每一项单独的进行比较
// Conditional2<Fish>-> Water
//Conditional2<Bird>-> Sky
// 结果是 Water | Sky
type R1 = Conditional2<Fish | Bird>;

//默认情况下 有些时候我们需要关闭这种分发能力,避免造成不准确的判断
type Conditional3<T, U> = T extends U ? true : false;
// Conditional3<1 | 2, 1 | 2 | 3>;将联合类型中的每一项单独的进行比较
// Conditional3< 2, 1 | 2 | 3-> true
// Conditional3< 1, 1 | 2 | 3-> true
// 结果是 true | true
type R2 = Conditional3<1 | 2, 1 | 2 | 3>;

// 默认情况下 有些时候我们需要关闭这种分发能力,避免造成不准确的判断
type Conditional4<T, U> = T extends U ? true : false;
// Conditional3<1 | 2, 1 | 2 | 3>;将联合类型中的每一项单独的进行比较
// Conditional3< 2, 1 -> false
// Conditional3< 1, 1 -> true
// 结果是 false | true ===> boolean
type R3 = Conditional4<1 | 2, 1>;

// 禁用分发
// 方案一
type NoDistribute<T> = T & {}; // ===> 这样可以使独立的联合类型的T,变成交集
type Conditional5<T, U> = NoDistribute<T> extends U ? true : false;
type R4 = Conditional5<1 | 2, 1>;
// 方案二
type Conditional6<T, U> = [T] extends [U] ? true : false; // ===> 这样可以使T变成元数组,不在裸露
type R5 = Conditional5<1 | 2, 1>;

//条件判断还有一些注意事项
// never直接比较的时候无法返回正确的结果
type IsNever<T> = T extends never ? true : false;
// 需要包装一下
// type IsNever<T> = NoDistribute<T> extends never ? true : false;
type R6 = IsNever<never>;
//我们在进行类型父子关系的比较时，默认情况下都应该关闭分发

//通过条件类型，ts自己实现了一些常见的内置类型...................................................................
//我们在使用ts的时候需要安装typescript模块(包含了很多的内置类型)

// 内置1:Extract
// T 和U 没有硬性的关系
// type MyExtract<T,U>= T extends u ? T : never;
type R7 = Extract<1 | 2 | 3, 1 | 2 | 4>; //求差集 用第一个和第二个类型的公共部分
// 内置2:Exclude

// type MyExclude<T,U>= T extends U ? never : T;
type R8 = Exclude<1 | 2 | 3 | 4 | 5, 2 | 4>;
// 内置3:NonNullable
// NonNullable 旧版 type MyExtract<T, U> = T extends null | undefined ? never : T;
// NonNullable 新版 type MyExtract<T> = T & {}; // 1) null & {}=>never; 2) 1 & {}=>1; 3) 1| never => 1
type R9 = NonNullable<1 | 2 | null | undefined>;

// infer 类型推断................................................................................................................................
// infer可以在条件类型中提取类型的某一个部分，在使用的时候想获取什么类型就将他写在什么“地方"加一个变量可以自动的来推导,类型推导都是基于位置的
// 1.获取函数的返回值类型
function getObj(name: string, age: number) {
  return { name, age };
}
// infer可以在条件类型中提取类型的某一个部分，
// 在使用的时候想获取什么类型就将他写在什么“地方"加一个变量可以自动的来推导,类型推导都是基于位置的 infer R
// 泛型约束的目的，是限制泛型的传入，后面的条件是逻辑

// 提取返回值类型
type ReturnType<T extends (...args: any[]) => any> = T extends (
  ...args: any[]
) => infer R
  ? R
  : never;
type R10 = ReturnType<typeof getObj>; // 使用infer 需要先创造一个条件才可以

// 提取参数类型
type Parameter<T extends (...args: any[]) => any> = T extends (
  ...args: infer R
) => any
  ? R
  : never;
type R11 = ReturnType<typeof getObj>; // 使用infer 需要先创造一个条件才可以

// 取类本身的类型判断构造函数的参数
abstract class A {
  constructor(name: string, age: number) {}
}
// 取类本身的类型判断构造函数的参数
type ConstructorParameters<T extends abstract new (...args: any[]) => any> =
  T extends abstract new (...args: infer P) => any ? P : never;
type R12 = ConstructorParameters<typeof A>;
//  取类本身的类型判断构造函数的的实例
type InstanceType<T extends abstract new (...args: any[]) => any> =
  T extends abstract new (...args: any[]) => infer p ? p : never;
type R13 = InstanceType<typeof A>;
function createInstance<T extends new (...args: any[]) => any>(
  target: T,
  ...args: ConstructorParameters<T>
): InstanceType<T> {
  return new target(...args);
}
class MyPerson {
  constructor(public name: string, public age: number) {}
}
let r = createInstance(MyPerson, "jiagwen", 30);

// infer中实现了很多的内置类型(ReturnType,Paramaters,ConstructorParameters,InstanceType)
// infer的使用
type Swap<T> = T extends [infer A1, infer A2] ? [A2, A1] : never;
type R14 = Swap<["jw", 30]>; // 30 ，'jw'
// 头尾交换
type SwapHeadTail1<T> = T extends [infer H, ...infer N, infer T]
  ? [T, ...N, H]
  : never;
type R15 = SwapHeadTail1<[1, 2, 3, 4, 5, 6, 7]>;
// promise 如果返回的是一个promise 会不停地解析这个promsie

//promise 如果返回的是一个promise 会不停地解析这个promsie
type PromiseReturnValue<T> = T extends Promise<infer P>
  ? PromiseReturnValue<P>
  : T;
type R16 = PromiseReturnValue<Promise<Promise<Promise<100>>>>;
//通过infer来实现递归推导
//将元组转化成联合类型[number,boolean,string]=>number| boolean string
// 方法一
type TupleToArray1 = [number, boolean, string][number];
// 方法二
type ElementToUnion<T> = T extends Array<infer E> ? E : never;
type TupleToArray2 = ElementToUnion<[number, boolean, string]>;

// 循环
interface IAddress1 {
  n: 501;
  x: 100;
  y: 100;
}
interface Person {
  name: string;
  age: number;
  address: IAddress1;
}
// 实现内置Partial
type Partial<T> = {
  // [K in keyof T] 相当于 for key in name |  age
  [K in keyof T]?: T[K]; // 通过索引查询拿到值
};
// 实现内置Required
type Required<T> = {
  [K in keyof T]-?: T[K];
};
// 实现内置Readonly
type Readonly<T> = {
  // +readonly [K in keyof T]: T[K];
  // readonly [K in keyof T]: T[K];
};
type ReadonlyRes = Readonly<IPerson>;
// 实现内置Mutate
// 不能修改就是readonly ， 却掉readonly
type Mutate<T> = {
  -readonly [K in keyof T]: T[K];
};

// 实现自定义DeepPartial
type DeepPartial<T> = {
  // [K in keyof T] 相当于 for key in name |  age
  [K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K]; // 通过索引查询拿到值
};
// 实战
type PartialPerson = DeepPartial<Person>; // 循环所有的属性 增加可选参数
let person: PartialPerson = {
  name: "jiangwen",
}; // Partial Required Readonly 属性修饰符readonly// Pick Omit Record

let person2: Required<PartialPerson> = {
  name: "jiangwen",
  age: 30,
  address: {},
};
let person3: Readonly<Required<PartialPerson>> = {
  name: "jiangwen",
  age: 30,
  address: {},
};

//Pick Omit 重构对象的结构 可以采用这两个类型
type Pick<T, K extends keyof T> = {
  [key in K]: T[key];
};
type PickPerson = Pick<Person, "name" | "age">;
let person4: PickPerson;
// 在很多属性中挑选需要的，在很多属性中排出不需要的
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
type OmitPerson = Omit<Person, "address">;
let person5: OmitPerson;
//映射类型 Pick + Omit 配合 Extract 和 Exclude 可以实现各种各样的类型

// Omit<T, keyof K> & K 将K 有的属性 在T 里面移除掉 然后再& K添加回去
function mixin<T, K>(a: T, b: K): Omit<T, keyof K> & K {
  return { ...a, ...b };
}
let x = mixin(
  { name: "jiangwen", age: 30, c: 3 },
  { name: 123, age: 30, b: 2 }
);
type Computed<T> = {
  // 通过这种创建一个新对象的方式更直观的看结果
  [K in keyof T]: T[K];
};
type nameType = Computed<typeof x>;
// type nameType = typeof x

// typeof 取类型的
// 索引查询 []
// in 循环的
// extends 条件

//只想要key-> value的格式 可以采用 Record类型

type Record<K extends keyof any, V> = { [P in K]: V }; // 就是普通的对象接口可以传入key的类型以及值的类型
//=-= 任意接口
let person6: Record<string, any> = { abc: 123 };

function map<T extends keyof any, K, R>(
  obj: Record<T, K>,
  callback: (value: K, key: T) => R
) {
  let result = {} as Record<T, R>;
  for (let key in obj) {
    result[key] = callback(obj[key], key);
  }
  return result;
}
let mapResult = map({ name: "jiangwen", age: 30 }, (value, key) => {
  return { a: 1 };
}); //name> 'abc',age: 'abc
